/*
 *    Geotoolkit - An Open Source Java GIS Toolkit
 *    http://www.geotoolkit.org
 *
 *    (C) 2014, Geomatys
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotoolkit.gui.javafx.render2d.shape;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Paint;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Polyline;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.shape.StrokeType;
import org.geotoolkit.gui.javafx.util.FXUtilities;

/**
 * Convert a JTS Geometry in a JavaFX Shape.
 * JavaFX has a very limited geometry model, multi part geometries and holes
 * can not be create directly.
 * As a result the jts geometry is translated as a group of smaller geometries.
 *
 * @author Johann Sorel (Geomatys)
 */
public class FXGeometry extends Group {

    private final Geometry geometry;
    private final Node shape;

    public FXGeometry(Geometry geometry) {
        this.geometry = geometry;
        this.shape = toShape(geometry);
        getChildren().add(shape);
    }

    public void setFill(final Paint paint){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setFill(paint);});
    }

    public void setStroke(final Paint paint){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStroke(paint);});
    }

    public void setStrokeDashOffset(final double offset){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeDashOffset(offset);});
    }

    public void setStrokeLineCap(final StrokeLineCap cap){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeLineCap(cap);});
    }

    public void setStrokeLineJoin(final StrokeLineJoin join){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeLineJoin(join);});
    }

    public void setStrokeMiterLimit(final double value){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeMiterLimit(value);});
    }

    public void setStrokeType(final StrokeType type){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeType(type);});
    }

    public void setStrokeWidth(final double value){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setStrokeWidth(value);});
    }

    public void setSmooth(final boolean smooth){
        FXUtilities.visit(this, (Node n) -> {if(n instanceof Shape)((Shape)n).setSmooth(smooth);});
    }


    private static Node toShape(Geometry jts){

        if(jts.isEmpty()){
            //do nothing
            return new Group();
        }else if(jts instanceof Point){
            final Point geom = (Point) jts;
            final Path fxgeom = new Path();
            fxgeom.getElements().add(new MoveTo(geom.getX(),geom.getY()));
            fxgeom.getElements().add(new LineTo(geom.getX(),geom.getY()));
            fxgeom.setCache(false);
            return fxgeom;

        }else if(jts instanceof LineString){
            final LineString geom = (LineString) jts;
            return toShape(geom,false);

        }else if(jts instanceof Polygon){
            //append exterior
            final Polygon geom = (Polygon) jts;
            final LineString exterior = geom.getExteriorRing();
            Shape fxgeom = toShape(exterior, true);
            //remove holes
            final int nbHole = geom.getNumInteriorRing();
            for(int i=0;i<nbHole;i++){
                final LineString interior = geom.getInteriorRingN(i);
                fxgeom = Shape.subtract(fxgeom, toShape(interior, true));
            }
            fxgeom.setCache(false);
            return fxgeom;

        }else if(jts instanceof GeometryCollection){
            final GeometryCollection geom = (GeometryCollection)jts;
            final Group fxgeom = new Group();
            final int nbGeom = geom.getNumGeometries();
            for(int i=0;i<nbGeom;i++){
                fxgeom.getChildren().add(toShape(geom.getGeometryN(i)));
            }
            fxgeom.setCache(false);
            return fxgeom;
        }else{
            throw new IllegalArgumentException("Unexpected geometry type : "+jts);
        }
    }

    private static Shape toShape(LineString geom, boolean closed){
        final Coordinate[] coords = geom.getCoordinates();
        final double[] vals = new double[coords.length*2];
        for(int x=0,i=0;i<coords.length;i++){
            vals[x++] = coords[i].x;
            vals[x++] = coords[i].y;
        }
        final Shape fxgeom;
        if(closed){
            fxgeom = new javafx.scene.shape.Polygon(vals);

        }else{
            fxgeom = new Polyline(vals);
        }
        fxgeom.setCache(false);
        return fxgeom;
    }

}
